﻿/* 
 * Shuriken is the legal property of its developers. Please refer to the
 * COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.IO;
using System.Xml;
using System.Text;
using System.ComponentModel;
using System.Collections.Generic;

namespace Shuriken.Core.Preferences
{
    /// <summary>
    /// Generic class to implement a serializable dictionary like property store
    /// </summary>
    public class XmlPropertiesBackend : IPreferencesBackend
    {
        private static string ROOT = "dictionary";
        private static string ENTRY = "property";
        private static string KEY = "key";
        private static string VALUE = "value";

        private Dictionary<string, object> dictionary = new Dictionary<string, object>(); 

        public void Save(string fileName)
        {
            using (XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
            {
                writer.Formatting = Formatting.Indented;
                writer.WriteStartElement(XmlPropertiesBackend.ROOT);

                List<KeyValuePair<string, object>> sortedProperties = new List<KeyValuePair<string, object>>(dictionary);
                sortedProperties.Sort((a,b) => StringComparer.OrdinalIgnoreCase.Compare(a.Key, b.Key));
                foreach (KeyValuePair<string, object> property in sortedProperties)
                {
                    object value = property.Value;
                    string key = property.Key;

                    writer.WriteStartElement(XmlPropertiesBackend.ENTRY);
                    writer.WriteAttributeString(XmlPropertiesBackend.KEY, key);
                    WritePropertyValue(writer, value);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }
        }

        private void WritePropertyValue(XmlTextWriter writer, object value)
        {
            if (value != null)
            {
                TypeConverter valueConverter = TypeDescriptor.GetConverter(value);

                if (valueConverter.CanConvertFrom(typeof(string)))
                {
                    string val = valueConverter.ConvertToInvariantString(value);
                    writer.WriteAttributeString(XmlPropertiesBackend.VALUE, val);
                }
                else
                {
                    throw new NotImplementedException("Non string convertable properties aren ot implemented!");
                }
            }
        }
        

        public static XmlPropertiesBackend Load(string fileName)
        {
            if (!File.Exists(fileName))
            {
                return null;
            }

            using (XmlTextReader reader = new XmlTextReader(fileName))
            {
                XmlPropertiesBackend prop = null;

                while (reader.Read())
                {
                    if (reader.IsStartElement() && reader.LocalName.Equals(XmlPropertiesBackend.ROOT))
                    {
                        prop = new XmlPropertiesBackend();
                        prop.ReadProperties(reader, XmlPropertiesBackend.ROOT);
                    }
                }

                return prop;
            }
        }

        private void ReadProperties(XmlTextReader reader, string endElement)
        {
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.EndElement:
                        if (reader.LocalName.Equals(endElement))
                        {
                            return;
                        }
                        break;
                    case XmlNodeType.Element:
                        if (reader.LocalName.Equals(XmlPropertiesBackend.ENTRY))
                        {
                            string key = reader.GetAttribute(XmlPropertiesBackend.KEY);
                            string value = reader.GetAttribute(XmlPropertiesBackend.VALUE);
                            dictionary.Add(key, value);
                        }
                        break;
                }
            }
        }



        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("Properties[");

            foreach (KeyValuePair<string, object> v in dictionary)
            {
                sb.Append(v.Key);
                sb.Append("=");
                sb.Append(v.Value);
                sb.Append(",");                
            }
            sb.Append("]");

            return sb.ToString();

        }
        #region IPreferencesBackend Members

        public bool TryGet<T>(string key, out T value)
        {
            value = default(T);

            if (string.IsNullOrEmpty(key))
            {
                return false;
            }

            if (!dictionary.ContainsKey(key))
            {
                return false;
            }

            object o = dictionary[key];
            Type t = typeof(T);

            if (o.GetType().Equals(t))
            {
                value = (T)o;
            }
            else if(o is string)
            {
                string val = (string)o;
                TypeConverter c = TypeDescriptor.GetConverter(t);
                value = (T)c.ConvertFromInvariantString(val); 
            }

            return true;
        }

        public bool TrySet<T>(string key, T value)
        {
            if (dictionary.ContainsKey(key))
            {
                dictionary[key] = value;
                return true;
            }
            else
            {
                dictionary.Add(key, value);
                return true;
            }
        }

        #endregion
    }
}
